package org.cainiao.basic.concurrent.lock.synchronize;

import static org.cainiao.util.Util.println;
import static org.cainiao.util.Util.silentSleep;

/**
 * 重入后，线程t1在monitor上的count为2
 * 线程t1在同步块中调用monitor.wait();会清空monitor上的owner以及让count变为0
 * 从而线程main可以进入同步块
 * 线程main在同步块中调用monitor.notify();
 * 虽然原来线程t1在wait之前，在monitor上count为2
 * 但是当线程main没有出同步块时，owner还是main，线程t1在wait的地方等待
 * 线程main出同步块时会唤醒所有等待锁的线程，这里是线程t1
 * 线程t1获得锁之后，monitor上的owner为t1，同时count会被恢复为2，而不是1
 * 所以线程t1第一次出同步块时，count为1，线程t3只能等待
 * 直到线程t2第二次出同步块，线程t3才能获得锁
 */
public class WaitNotifyTest {

    private static final Object monitor = new Object();

    public static void main(String[] args) throws InterruptedException {
        new Thread(WaitNotifyTest::f1).start();
        Thread.sleep(1000);
        synchronized (monitor) {
            println("main() >>> 1");
            monitor.notify();
            Thread.sleep(1000);
            println("main() >>> 2");
        }
    }

    private static void f1() {
        synchronized (monitor) {
            println("f1() >>> 1");
            f2();
            println("f1() >>> 2");
            silentSleep(5000);
            println("f1() >>> 3");
        }
        println("f1() >>> 4");
    }

    private static void f2() {
        synchronized (monitor) {
            println("f2() >>> 1");
            try {
                monitor.wait();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            println("f2() >>> 2");
        }
        new Thread(WaitNotifyTest::f3).start();
        println("t2 started, out of synchronized");
        silentSleep(1000);
    }

    private static void f3() {
        synchronized (monitor) {
            println("f3() >>> 1");
            silentSleep(3000);
            println("f3() >>> 2");
        }
    }
}
